package cn.icybear.orzjisp.bytefile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;

public class DefualtSection extends ByteArrayOutputStream implements Section,Cloneable {
	protected int tag;
	protected DataInputStream inputStream;
	protected DataOutputStream outputStream;
	protected ByteBuffer readBuffer;
	public boolean read = false;

	public DefualtSection() {
		outputStream = new DataOutputStream(this);
	}

	public DefualtSection(DataInputStream is) throws IOException {
		read = true;
		this.tag = is.readUnsignedByte();
		int len = is.readInt();
		readData(is, len);
	}

	protected void readData(DataInputStream is,int len) throws IOException{
//		System.out.println("Tag="+tag);
//		System.out.println("SectLen="+len);
		buf = new byte[len];
		is.readFully(buf);
		readBuffer = ByteBuffer.wrap(buf).asReadOnlyBuffer();
		inputStream = new DataInputStream(new ByteArrayInputStream(buf));
	}
	
	public int getTag() {
		return tag;
	}

	public void setTag(int tag) {
		this.tag = tag;
	}

	@Override
	public synchronized void write(int b) {
		if (read)
			throw new RuntimeException("ReadOnly Section");
		super.write(b);
	}

	@Override
	public void write(byte[] b) throws IOException {
		if (read)
			throw new RuntimeException("ReadOnly Section");
		super.write(b);
	}

	public void write(byte[] b, int off, int len) {
		if (read)
			throw new RuntimeException("ReadOnly Section");
		super.write(b, off, len);
	}

	public ByteBuffer getReadBuffer() {
		if (!read)
			throw new RuntimeException("WriteOnly Section");
		return readBuffer;
	}

	public DataInputStream getInputStream() {
		if (!read)
			throw new RuntimeException("WriteOnly Section");
		return inputStream;
	}

	public ByteArrayOutputStream getWriteBuffer() {
		return this;
	}

	public DataOutputStream getOutputStream() {
		return outputStream;
	}

	/**
	 * Total length of this section, including tag and data_len
	 * 
	 * @return how many bytes.
	 */
	public int getLength() {
		return size() + 5;
	}

	/**
	 * Data size of this section, excluding tag and data_len
	 */
	@Override
	public synchronized int size() {
		return super.size();
	}

	/**
	 * Sub-class should overwrite this method to write it's own header before data
	 * @param os
	 * @throws IOException
	 */
	protected void writeAddionalHeader(DataOutputStream os) throws IOException {

	}

	/**
	 * pass a DataOutputStream if possible.
	 */
	public void writeTo(OutputStream out) throws IOException {
		DataOutputStream writer = (out instanceof DataOutputStream) ? (DataOutputStream) out
				: new DataOutputStream(out);
		writer.writeByte(tag);
		writer.writeInt(size());
		writeAddionalHeader(writer);
		super.writeTo(writer);
	}

	@Override
	public DefualtSection clone() {
		DefualtSection s;
		try {
			s = (DefualtSection) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
			return null;
		}
		s.readBuffer = readBuffer.duplicate();
		s.inputStream = new DataInputStream(new ByteArrayInputStream(buf));
		return s;
	}

	@Override
	public String toString() {
		return ("Tag=" + tag + ";Len=" + size());
	}

	@Override
	public boolean isReadonly() {
		return read;
	}
	
	public static Section readSection(DataInputStream is) throws IOException{
		is.mark(4);
		int tag = is.readUnsignedByte();
//		System.out.println("TAG="+tag);
		is.reset();
		switch (tag) {
		case TAG_CODE:
			return (new CodeSection(is));
		case TAG_LINE_NUMBER_TABLE:
			return (new LineNumberTable(is));
		default:
			return (new DefualtSection(is));
		}
	}
}
